package com.ocean.web.util.rwdata;

import com.ocean.system.domain.SeaTempNcMetadata;
import ucar.ma2.Array;
import ucar.nc2.Attribute;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 西北太平洋大气数值产品元数据读取工具（适配 netcdfAll-5.4.1 + NMF_BEN_SA_NWPACDT 格式）
 * 沿用 SeaTempNcMetadata 实体类，适配气压、10米风要素（要素返回分号隔开短名称）
 */
public class GlobalXBDQSZNcReader_5_4_1 {

    // 时间格式工具（适配文件全局属性的时间格式，CST显示）
    private static final SimpleDateFormat FILE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
    private static final SimpleDateFormat CST_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static final SimpleDateFormat FILENAME_TIME_FORMAT = new SimpleDateFormat("yyyyMMddHH");
    static {
        FILE_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        CST_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        FILENAME_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+8"));
    }

    // 大气产品文件名正则（兼容ATM_cropped和ATM_calculate后缀，避免解析失败）
    private static final Pattern FILENAME_PATTERN = Pattern.compile(
            "^NMF_BEN_SA_NWPACDT_(\\d{10})_(\\d{3}h)_ATM_(cropped|calculate)\\.nc$"
    );

    /**
     * 核心方法：读取西北太平洋大气 NC 文件元数据，返回 SeaTempNcMetadata 实体
     * @param ncFilePath NC 文件路径（本地文件）
     * @return SeaTempNcMetadata 元数据实体
     * @throws IOException NC 文件读取异常
     */
    public SeaTempNcMetadata readAtmMetadata(String ncFilePath) throws IOException {
        String ncFileName = extractFileName(ncFilePath);
        FileNameParseResult parseResult = parseFileName(ncFileName);

        try (NetcdfFile ncFile = NetcdfFile.open(ncFilePath)) {
            // 1. NC文件名（已提取）
            // 2. 来源（优先读文件history属性，默认西北太平洋大气数值预报系统）
            String source = getGlobalAttrValue(ncFile, "institution", "西北太平洋大气数值预报系统");
            if ("西北太平洋大气数值预报系统".equals(source)) {
                String history = getGlobalAttrValue(ncFile, "history", "");
                source = history.isEmpty() ? "西北太平洋大气数值预报系统" : "西北太平洋PCWRF模式输出（" + history.substring(0, Math.min(50, history.length())) + "...）";
            }

            // 3. 类型（西北太平洋大气数值产品）
            String type = "西北太平洋大气数值产品（气压+10米风）";

            // 4. 起报时间（文件名优先，失败则读全局属性START_DATE/SIMULATION_START_DATE）
            String forecastBaseTime = parseResult.getBaseTime();
            if ("解析失败".equals(forecastBaseTime)) {
                forecastBaseTime = parseBaseTimeFromGlobalAttr(ncFile);
            }

            // 5. 预报时效（文件名168h优先，结合全局属性修正：扣除24小时同化场）
            String forecastLeadTime = parseResult.getLeadTime();
            if ("解析失败".equals(forecastLeadTime)) {
                forecastLeadTime = calculateLeadTimeFromTimeDim(ncFile);
            } else {
                // 补充同化场说明（全局属性NOTES）
                String notes = getGlobalAttrValue(ncFile, "NOTES", "");
                if (!notes.isEmpty()) {
                    forecastLeadTime += "（" + notes + "）";
                }
            }

            // 6. 预报频次（总时长/时次，总时长=同化场24h+预报场168h=192h，时次193个→每1小时1次）
            String forecastFreq = calculateForecastFreq(ncFile);

            // 7. 覆盖范围（西北太平洋海域，解析longitude/latitude实际范围）
            String coverage = parseCoverage(ncFile);

            // 8. 空间分辨率（计算longitude/latitude格点间隔）
            String spatialRes = calculateSpatialResolution(ncFile);

            // 9. 预报要素（短名称，分号隔开）- 核心修改
            String elements = getAtmShortNames(ncFile);

            // 构建实体返回
            return new SeaTempNcMetadata(
                    ncFileName,
                    source,
                    type,
                    forecastBaseTime,
                    forecastLeadTime,
                    forecastFreq,
                    coverage,
                    spatialRes,
                    elements
            );
        }
    }

    // ---------------------- 核心修改：提取大气要素短名称（分号隔开）----------------------
    /**
     * 提取西北太平洋大气要素短名称（纯短名称，分号隔开）
     * 适配：原始变量（SLP/U10/V10）+ 任务类计算后变量（wspd10/wdir10），自动去重
     */
    private String getAtmShortNames(NetcdfFile ncFile) {
        // 大气核心变量名（覆盖原始+计算后变量）
        String[] targetVarNames = {"SLP", "U10", "V10", "wspd10", "wdir10"};
        StringBuilder shortNames = new StringBuilder();

        for (String varName : targetVarNames) {
            Variable var = ncFile.findVariable(varName);
            if (var == null) {
                continue; // 变量不存在则跳过，避免空指针
            }

            // 提取短名称：优先取变量shortName，无则用变量名（变量名即标准短名称）
            String shortName = var.getShortName() != null ? var.getShortName().trim() : varName.trim();

            // 去重：避免重复添加同一短名称（严格匹配变量名大小写，符合NC文件规范）
            if (shortNames.indexOf(shortName) == -1) {
                shortNames.append(shortName).append(";");
            }
        }

        // 移除最后一个分号，无要素时返回默认短名称
        if (shortNames.length() > 0) {
            return shortNames.substring(0, shortNames.length() - 1);
        } else {
            return "SLP;U10;V10"; // 无要素时返回默认核心短名称
        }
    }

    // ---------------------- 原有工具方法（仅删除重复方法，其余不变）----------------------
    /**
     * 提取文件名
     */
    private String extractFileName(String filePath) {
        if (filePath == null || filePath.isEmpty()) {
            return "unknown.nc";
        }
        filePath = filePath.replace("\\", "/");
        int lastSlashIndex = filePath.lastIndexOf("/");
        return lastSlashIndex == -1 ? filePath : filePath.substring(lastSlashIndex + 1);
    }

    /**
     * 解析大气产品文件名：起报时间、预报时效
     */
    private FileNameParseResult parseFileName(String fileName) {
        Matcher matcher = FILENAME_PATTERN.matcher(fileName);
        if (!matcher.matches()) {
            return new FileNameParseResult("解析失败", "解析失败");
        }

        try {
            // 解析起报时间（2025111100 → 2025-11-11 00:00:00 CST）
            String baseTimeStr = matcher.group(1);
            Date baseTime = FILENAME_TIME_FORMAT.parse(baseTimeStr);
            String formattedBaseTime = CST_FORMAT.format(baseTime);

            // 解析预报时效（168h → 168小时）
            String leadTimeStr = matcher.group(2);
            String formattedLeadTime = leadTimeStr.replace("h", "小时");

            return new FileNameParseResult(formattedBaseTime, formattedLeadTime);
        } catch (ParseException e) {
            return new FileNameParseResult("解析失败", "解析失败");
        }
    }

    /**
     * 从全局属性读取起报时间（适配START_DATE/SIMULATION_START_DATE）
     */
    private String parseBaseTimeFromGlobalAttr(NetcdfFile ncFile) {
        // 优先读START_DATE
        String dateStr = getGlobalAttrValue(ncFile, "START_DATE", (String) null);
        if (dateStr == null) {
            dateStr = getGlobalAttrValue(ncFile, "SIMULATION_START_DATE", (String) null);
        }
        if (dateStr == null) {
            return "未知";
        }

        try {
            // 适配文件格式：2025-09-08_00:00:00 → 转换为CST格式
            Date date = FILE_TIME_FORMAT.parse(dateStr);
            return CST_FORMAT.format(date);
        } catch (ParseException e) {
            return dateStr; // 解析失败返回原始值
        }
    }

    /**
     * 从time维度计算预报时效（总时次时长-24小时同化场）
     */
    private String calculateLeadTimeFromTimeDim(NetcdfFile ncFile) {
        int timeSize = getTimeDimSize(ncFile);
        if (timeSize <= 0) {
            return "未知";
        }

        // 全局属性说明：前24小时为同化场，剩余为预报场
        String notes = getGlobalAttrValue(ncFile, "NOTES", "");
        // 总时长=193个时次（每1小时1次）→ 192小时，预报时效=192-24=168小时（符合文件名）
        int totalHours = timeSize - 1; // 时次间隔1小时，193个时次=192小时
        int forecastHours = totalHours - 24;

        String leadTime = String.format("%d小时（总时长%d小时，含24小时同化场）", forecastHours, totalHours);
        return notes.isEmpty() ? leadTime : leadTime + "（" + notes + "）";
    }

    /**
     * 计算预报频次（基于时次间隔和总时长推导）
     */
    private String calculateForecastFreq(NetcdfFile ncFile) {
        int timeSize = getTimeDimSize(ncFile);
        if (timeSize < 2) {
            return "单次（分析场）";
        }

        // 从全局属性和时次推导：193个时次=192小时→每1小时1次
        int totalHours = timeSize - 1;
        double interval = (double) totalHours / (timeSize - 1);
        if (interval == 1) {
            return "每1小时1次（含同化场+预报场）";
        } else {
            return String.format("每%.0f小时1次（总时次：%d个）", interval, timeSize);
        }
    }

    /**
     * 获取time维度长度
     */
    private int getTimeDimSize(NetcdfFile ncFile) {
        // 先从time变量获取维度
        Variable timeVar = ncFile.findVariable("time");
        if (timeVar != null) {
            return timeVar.getShape()[0];
        }
        // 直接从维度获取
        return ncFile.findDimension("time") != null ? ncFile.findDimension("time").getLength() : 0;
    }

    /**
     * 解析覆盖范围（西北太平洋 + longitude/latitude实际范围）
     */
    private String parseCoverage(NetcdfFile ncFile) {
        String latRange = getLatLonRange(ncFile, "latitude");
        String lonRange = getLatLonRange(ncFile, "longitude");

        return String.format("西北太平洋海域（纬度：%s，经度：%s）", latRange, lonRange);
    }

    /**
     * 获取经/纬度范围（适配longitude/latitude变量名）
     */
    private String getLatLonRange(NetcdfFile ncFile, String varName) {
        Variable var = ncFile.findVariable(varName);
        if (var == null) {
            return "未知";
        }

        try {
            Array array = var.read();
            double min = Double.MAX_VALUE;
            double max = Double.MIN_VALUE;

            // 遍历数组取真实min/max（避免维度递增/递减影响）
            for (int i = 0; i < array.getSize(); i++) {
                double val = array.getFloat(i); // 变量类型为float
                if (val != -9999.0f && val < min) min = val; // 排除填充值
                if (val != -9999.0f && val > max) max = val;
            }

            // 补充单位说明（degree_north/degree_east）
            String unit = getVarAttrValue(var, "units", "");
            if ("degree_north".equals(unit)) {
                return String.format("%.2f°N - %.2f°N", min, max);
            } else if ("degree_east".equals(unit)) {
                return String.format("%.2f°E - %.2f°E", min, max);
            } else {
                return String.format("%.2f - %.2f（%s）", min, max, unit);
            }
        } catch (Exception e) {
            return "解析失败";
        }
    }

    /**
     * 计算空间分辨率（longitude/latitude格点间隔）
     */
    private String calculateSpatialResolution(NetcdfFile ncFile) {
        String latRes = getDimResolution(ncFile, "latitude");
        String lonRes = getDimResolution(ncFile, "longitude");

        return String.format("纬度：%s，经度：%s", latRes, lonRes);
    }

    /**
     * 计算单个维度的分辨率（相邻格点间隔）
     */
    private String getDimResolution(NetcdfFile ncFile, String varName) {
        Variable var = ncFile.findVariable(varName);
        if (var == null) {
            return "未知";
        }

        try {
            Array array = var.read();
            int size = (int) array.getSize();
            if (size < 2) {
                return "未知";
            }

            // 跳过填充值（-9999.0f），找有效相邻格点
            double val1 = 0, val2 = 0;
            int count = 0;
            for (int i = 0; i < size; i++) {
                double val = array.getFloat(i);
                if (val != -9999.0f) {
                    if (count == 0) {
                        val1 = val;
                        count++;
                    } else if (count == 1) {
                        val2 = val;
                        count++;
                        break; // 找到两个有效值即可
                    }
                }
            }

            if (count < 2) {
                return "未知";
            }

            double res = Math.abs(val2 - val1);
            return String.format("%.2f°", res); // 大气产品分辨率通常保留2位小数
        } catch (Exception e) {
            return "解析失败";
        }
    }

    /**
     * 读取全局属性值（保留1个，删除重复项→编译报错修复）
     */
    private String getGlobalAttrValue(NetcdfFile ncFile, String attrName, String defaultValue) {
        Attribute attr = ncFile.findGlobalAttribute(attrName);
        return attr != null ? attr.getStringValue().trim() : defaultValue;
    }

    /**
     * 读取全局属性值（多默认值数组版本，保留）
     */
    private String getGlobalAttrValue(NetcdfFile ncFile, String attrName, String[] defaultValues) {
        Attribute attr = ncFile.findGlobalAttribute(attrName);
        if (attr != null) {
            return attr.getStringValue().trim();
        }
        // 遍历默认值数组，返回第一个非空值
        for (String val : defaultValues) {
            if (val != null && !val.isEmpty()) {
                return val;
            }
        }
        return "";
    }

    /**
     * 读取变量属性值
     */
    private String getVarAttrValue(Variable var, String attrName, String defaultValue) {
        Attribute attr = var.findAttribute(attrName);
        return attr != null ? attr.getStringValue().trim() : defaultValue;
    }

    /**
     * 内部辅助类：存储文件名解析结果
     */
    private static class FileNameParseResult {
        private final String baseTime;
        private final String leadTime;

        public FileNameParseResult(String baseTime, String leadTime) {
            this.baseTime = baseTime;
            this.leadTime = leadTime;
        }

        public String getBaseTime() {
            return baseTime;
        }

        public String getLeadTime() {
            return leadTime;
        }
    }

    // 测试方法（直接运行无报错，验证短名称输出）
    public static void main(String[] args) {
        // 替换为你的西北太平洋大气 NC 文件路径（支持cropped或calculate后缀）
        String ncFilePath = "E:\\NMF_BEN_SA_NWPACDT_2025111100_168h_ATM_calculate.nc";
        GlobalXBDQSZNcReader_5_4_1 reader = new GlobalXBDQSZNcReader_5_4_1();

        try {
            SeaTempNcMetadata metadata = reader.readAtmMetadata(ncFilePath);
            // 打印元数据（要素为分号隔开的短名称）
            System.out.println("西北太平洋大气数值产品 NC 文件元数据：");
            System.out.println(metadata);

            // 结构化获取字段示例（验证短名称）
            System.out.println("\n单独获取字段：");
            System.out.println("起报时间：" + metadata.getForecastBaseTime());
            System.out.println("要素名称（短名称）：" + metadata.getElements()); // 输出示例：SLP;U10;V10;wspd10;wdir10
            System.out.println("预报时效：" + metadata.getForecastLeadTime());
        } catch (IOException e) {
            System.err.println("读取失败：" + e.getMessage());
            e.printStackTrace();
        }
    }
}